From d66ad8c39dc37b5c7b014de5c3b8ae96aae87c49 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Wed, 19 Oct 2011 21:44:38 +0200 Subject: [PATCH] win32: Better crossing events and grab destination reporting We new report to the right window during !owner_event grabs, and we send proper enter and leave events. --- gdk/win32/gdkevents-win32.c | 390 +++++++++++++++++++++++++++++------- 1 file changed, 314 insertions(+), 76 deletions(-) diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c index b33b1764cf..5ea42453da 100644 --- a/gdk/win32/gdkevents-win32.c +++ b/gdk/win32/gdkevents-win32.c @@ -119,7 +119,7 @@ static GSourceFuncs event_funcs = { GPollFD event_poll_fd; -static GdkWindow *current_toplevel = NULL; +static GdkWindow *mouse_window = NULL; static gint current_x, current_y; static gint current_root_x, current_root_y; static UINT client_message; @@ -137,13 +137,6 @@ static UINT sync_timer = 0; static int debug_indent = 0; -static void -synthesize_enter_or_leave_event (GdkWindow *window, - MSG *msg, - GdkEventType type, - GdkCrossingMode mode, - GdkNotifyType detail); - static void assign_object (gpointer lhsp, gpointer rhs) @@ -506,48 +499,47 @@ static GdkWindow * find_window_for_mouse_event (GdkWindow* reported_window, MSG* msg) { - HWND hwnd; - POINTS points; POINT pt; - GdkWindow* other_window = NULL; GdkDeviceManagerWin32 *device_manager; + GdkWindow *event_window; + HWND hwnd; + RECT rect; + GdkDeviceGrabInfo *grab; device_manager = GDK_DEVICE_MANAGER_WIN32 (gdk_display_get_device_manager (_gdk_display)); - if (!_gdk_display_get_last_device_grab (_gdk_display, device_manager->core_pointer)) + grab = _gdk_display_get_last_device_grab (_gdk_display, device_manager->core_pointer); + if (grab == NULL) return reported_window; - points = MAKEPOINTS (msg->lParam); - pt.x = points.x; - pt.y = points.y; - ClientToScreen (msg->hwnd, &pt); - - hwnd = WindowFromPoint (pt); + pt = msg->pt; - if (hwnd != NULL) + if (!grab->owner_events) + event_window = grab->native_window; + else { - RECT rect; - - GetClientRect (hwnd, &rect); - ScreenToClient (hwnd, &pt); - if (!PtInRect (&rect, pt)) - return _gdk_root; + event_window = NULL; + hwnd = WindowFromPoint (pt); + if (hwnd != NULL) + { + POINT client_pt = pt; - other_window = gdk_win32_handle_table_lookup (hwnd); + ScreenToClient (hwnd, &client_pt); + GetClientRect (hwnd, &rect); + if (PtInRect (&rect, client_pt)) + event_window = gdk_win32_handle_table_lookup (hwnd); + } + if (event_window == NULL) + event_window = grab->native_window; } - if (other_window == NULL) - return _gdk_root; - /* need to also adjust the coordinates to the new window */ - pt.x = points.x; - pt.y = points.y; - ClientToScreen (msg->hwnd, &pt); - ScreenToClient (GDK_WINDOW_HWND (other_window), &pt); + ScreenToClient (GDK_WINDOW_HWND (event_window), &pt); + /* ATTENTION: need to update client coords */ msg->lParam = MAKELPARAM (pt.x, pt.y); - return other_window; + return event_window; } static void @@ -1137,39 +1129,230 @@ do_show_window (GdkWindow *window, gboolean hide_window) } static void -synthesize_enter_or_leave_event (GdkWindow *window, - MSG *msg, - GdkEventType type, - GdkCrossingMode mode, - GdkNotifyType detail) +send_crossing_event (GdkDisplay *display, + GdkWindow *window, + GdkEventType type, + GdkCrossingMode mode, + GdkNotifyType notify_type, + GdkWindow *subwindow, + POINT *screen_pt, + GdkModifierType mask, + guint32 time_) { GdkEvent *event; + GdkDeviceGrabInfo *grab; + GdkDeviceManagerWin32 *device_manager; POINT pt; - pt = msg->pt; + device_manager = GDK_DEVICE_MANAGER_WIN32 (gdk_display_get_device_manager (display)); + + grab = _gdk_display_has_device_grab (display, device_manager->core_pointer, 0); + + if (grab != NULL && + !grab->owner_events && + mode != GDK_CROSSING_UNGRAB) + { + /* !owner_event => only report events wrt grab window, ignore rest */ + if ((GdkWindow *)window != grab->native_window) + return; + } + + pt = *screen_pt; ScreenToClient (GDK_WINDOW_HWND (window), &pt); event = gdk_event_new (type); event->crossing.window = window; - event->crossing.subwindow = NULL; - event->crossing.time = _gdk_win32_get_next_tick (msg->time); + event->crossing.subwindow = subwindow; + event->crossing.time = _gdk_win32_get_next_tick (time_); event->crossing.x = pt.x; event->crossing.y = pt.y; - event->crossing.x_root = msg->pt.x + _gdk_offset_x; - event->crossing.y_root = msg->pt.y + _gdk_offset_y; + event->crossing.x_root = screen_pt->x + _gdk_offset_x; + event->crossing.y_root = screen_pt->y + _gdk_offset_y; + event->crossing.mode = mode; + event->crossing.detail = notify_type; event->crossing.mode = mode; - event->crossing.detail = detail; - event->crossing.focus = TRUE; /* FIXME: Set correctly */ - event->crossing.state = 0; /* FIXME: Set correctly */ + event->crossing.detail = notify_type; + event->crossing.focus = FALSE; + event->crossing.state = mask; gdk_event_set_device (event, _gdk_display->core_pointer); _gdk_win32_append_event (event); - + if (type == GDK_ENTER_NOTIFY && window->extension_events != 0) _gdk_device_wintab_update_window_coords (window); } +static GdkWindow * +get_native_parent (GdkWindow *window) +{ + if (window->parent != NULL) + return window->parent->impl_window; + return NULL; +} + +static GdkWindow * +find_common_ancestor (GdkWindow *win1, + GdkWindow *win2) +{ + GdkWindow *tmp; + GList *path1 = NULL, *path2 = NULL; + GList *list1, *list2; + + tmp = win1; + while (tmp != NULL && tmp->window_type != GDK_WINDOW_ROOT) + { + path1 = g_list_prepend (path1, tmp); + tmp = get_native_parent (tmp); + } + + tmp = win2; + while (tmp != NULL && tmp->window_type != GDK_WINDOW_ROOT) + { + path2 = g_list_prepend (path2, tmp); + tmp = get_native_parent (tmp); + } + + list1 = path1; + list2 = path2; + tmp = NULL; + while (list1 && list2 && (list1->data == list2->data)) + { + tmp = (GdkWindow *)list1->data; + list1 = g_list_next (list1); + list2 = g_list_next (list2); + } + g_list_free (path1); + g_list_free (path2); + + return tmp; +} + +void +synthesize_crossing_events (GdkDisplay *display, + GdkWindow *src, + GdkWindow *dest, + GdkCrossingMode mode, + POINT *screen_pt, + GdkModifierType mask, + guint32 time_, + gboolean non_linear) +{ + GdkWindow *c; + GdkWindow *win, *last, *next; + GList *path, *list; + GdkWindow *a; + GdkWindow *b; + GdkNotifyType notify_type; + + a = src; + b = dest; + if (a == b) + return; /* No crossings generated between src and dest */ + + c = find_common_ancestor (a, b); + + non_linear |= (c != a) && (c != b); + + if (a) /* There might not be a source (i.e. if no previous pointer_in_window) */ + { + /* Traverse up from a to (excluding) c sending leave events */ + if (non_linear) + notify_type = GDK_NOTIFY_NONLINEAR; + else if (c == a) + notify_type = GDK_NOTIFY_INFERIOR; + else + notify_type = GDK_NOTIFY_ANCESTOR; + send_crossing_event (display, + a, GDK_LEAVE_NOTIFY, + mode, + notify_type, + NULL, + screen_pt, + mask, time_); + + if (c != a) + { + if (non_linear) + notify_type = GDK_NOTIFY_NONLINEAR_VIRTUAL; + else + notify_type = GDK_NOTIFY_VIRTUAL; + + last = a; + win = get_native_parent (a); + while (win != c && win->window_type != GDK_WINDOW_ROOT) + { + send_crossing_event (display, + win, GDK_LEAVE_NOTIFY, + mode, + notify_type, + (GdkWindow *)last, + screen_pt, + mask, time_); + + last = win; + win = get_native_parent (win); + } + } + } + + if (b) /* Might not be a dest, e.g. if we're moving out of the window */ + { + /* Traverse down from c to b */ + if (c != b) + { + path = NULL; + win = get_native_parent (b); + while (win != c && win->window_type != GDK_WINDOW_ROOT) + { + path = g_list_prepend (path, win); + win = get_native_parent (win); + } + + if (non_linear) + notify_type = GDK_NOTIFY_NONLINEAR_VIRTUAL; + else + notify_type = GDK_NOTIFY_VIRTUAL; + + list = path; + while (list) + { + win = (GdkWindow *)list->data; + list = g_list_next (list); + if (list) + next = (GdkWindow *)list->data; + else + next = b; + + send_crossing_event (display, + win, GDK_ENTER_NOTIFY, + mode, + notify_type, + next, + screen_pt, + mask, time_); + } + g_list_free (path); + } + + + if (non_linear) + notify_type = GDK_NOTIFY_NONLINEAR; + else if (c == a) + notify_type = GDK_NOTIFY_ANCESTOR; + else + notify_type = GDK_NOTIFY_INFERIOR; + + send_crossing_event (display, + b, GDK_ENTER_NOTIFY, + mode, + notify_type, + NULL, + screen_pt, + mask, time_); + } +} + /* The check_extended flag controls whether to check if the windows want * events from extended input devices and if the message should be skipped * because an extended input device is active @@ -1716,7 +1899,7 @@ gdk_event_translate (MSG *msg, GdkWindow *window = NULL; GdkWindowImplWin32 *impl; - GdkWindow *orig_window, *new_window, *toplevel; + GdkWindow *orig_window, *new_window; GdkDeviceManager *device_manager; @@ -2157,7 +2340,29 @@ gdk_event_translate (MSG *msg, /* We keep the implicit grab until no buttons at all are held down */ if ((state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (button - 1))) == 0) - ReleaseCapture (); + { + ReleaseCapture (); + + new_window = NULL; + hwnd = WindowFromPoint (msg->pt); + if (hwnd != NULL) + { + POINT client_pt = msg->pt; + + ScreenToClient (hwnd, &client_pt); + GetClientRect (hwnd, &rect); + if (PtInRect (&rect, client_pt)) + new_window = gdk_win32_handle_table_lookup (hwnd); + } + synthesize_crossing_events (_gdk_display, + pointer_grab->native_window, new_window, + GDK_CROSSING_UNGRAB, + &msg->pt, + 0, /* TODO: Set right mask */ + msg->time, + FALSE); + assign_object (&mouse_window, new_window); + } } generate_button_event (GDK_BUTTON_RELEASE, button, @@ -2172,22 +2377,50 @@ gdk_event_translate (MSG *msg, (gpointer) msg->wParam, GET_X_LPARAM (msg->lParam), GET_Y_LPARAM (msg->lParam))); - assign_object (&window, find_window_for_mouse_event (window, msg)); - toplevel = gdk_window_get_toplevel (window); - if (current_toplevel != toplevel) + new_window = window; + + if (pointer_grab != NULL) + { + POINT pt; + pt = msg->pt; + + new_window = NULL; + hwnd = WindowFromPoint (pt); + if (hwnd != NULL) + { + POINT client_pt = pt; + + ScreenToClient (hwnd, &client_pt); + GetClientRect (hwnd, &rect); + if (PtInRect (&rect, client_pt)) + new_window = gdk_win32_handle_table_lookup (hwnd); + } + + if (!pointer_grab->owner_events && + new_window != NULL && + new_window != pointer_grab->native_window) + new_window = NULL; + } + + if (mouse_window != new_window) { - GDK_NOTE (EVENTS, g_print (" toplevel %p -> %p", - current_toplevel ? GDK_WINDOW_HWND (current_toplevel) : NULL, - toplevel ? GDK_WINDOW_HWND (toplevel) : NULL)); - if (current_toplevel) - synthesize_enter_or_leave_event (current_toplevel, msg, - GDK_LEAVE_NOTIFY, GDK_CROSSING_NORMAL, GDK_NOTIFY_ANCESTOR); - synthesize_enter_or_leave_event (toplevel, msg, - GDK_ENTER_NOTIFY, GDK_CROSSING_NORMAL, GDK_NOTIFY_ANCESTOR); - assign_object (¤t_toplevel, toplevel); - track_mouse_event (TME_LEAVE, GDK_WINDOW_HWND (toplevel)); + GDK_NOTE (EVENTS, g_print (" mouse_sinwod %p -> %p", + mouse_window ? GDK_WINDOW_HWND (mouse_window) : NULL, + new_window ? GDK_WINDOW_HWND (new_window) : NULL)); + synthesize_crossing_events (_gdk_display, + mouse_window, new_window, + GDK_CROSSING_NORMAL, + &msg->pt, + 0, /* TODO: Set right mask */ + msg->time, + FALSE); + assign_object (&mouse_window, new_window); + if (new_window != NULL) + track_mouse_event (TME_LEAVE, GDK_WINDOW_HWND (new_window)); } + assign_object (&window, find_window_for_mouse_event (window, msg)); + /* If we haven't moved, don't create any GDK event. Windows * sends WM_MOUSEMOVE messages after a new window is shows under * the mouse, even if the mouse hasn't moved. This disturbs gtk. @@ -2226,22 +2459,27 @@ gdk_event_translate (MSG *msg, GDK_NOTE (EVENTS, g_print (" %d (%ld,%ld)", HIWORD (msg->wParam), msg->pt.x, msg->pt.y)); - if (!gdk_win32_handle_table_lookup (WindowFromPoint (msg->pt))) - { - /* we are only interested if we don't know the new window */ - if (current_toplevel) - synthesize_enter_or_leave_event (current_toplevel, msg, - GDK_LEAVE_NOTIFY, GDK_CROSSING_NORMAL, GDK_NOTIFY_ANCESTOR); - assign_object (¤t_toplevel, NULL); - } - else if (window != gdk_window_get_toplevel (window)) /* xxx: only for native child windows? */ + new_window = NULL; + hwnd = WindowFromPoint (msg->pt); + if (hwnd != NULL) { - /* XXX: this used to be ignored pre-csw, but I think we need at least some - * of the leave events */ - synthesize_enter_or_leave_event (window, msg, - GDK_LEAVE_NOTIFY, GDK_CROSSING_NORMAL, GDK_NOTIFY_ANCESTOR); + POINT client_pt = msg->pt; + + ScreenToClient (hwnd, &client_pt); + GetClientRect (hwnd, &rect); + if (PtInRect (&rect, client_pt)) + new_window = gdk_win32_handle_table_lookup (hwnd); } + synthesize_crossing_events (_gdk_display, + mouse_window, new_window, + GDK_CROSSING_NORMAL, + &msg->pt, + 0, /* TODO: Set right mask */ + msg->time, + FALSE); + assign_object (&mouse_window, new_window); + return_val = TRUE; break; -- 2.30.2